import { Actor, ActorSystem, Context, PID, Props } from '../src';
import { SupervisorDirective } from '../src/actor/supervision';

// Define a custom supervisor strategy
class CustomSupervisor {
  handleFailure(pid: PID, reason: Error): SupervisorDirective {
    console.log(`[CustomSupervisor] Actor ${pid.id} failed with error:`, reason.message);
    
    // We can decide different strategies based on the error
    if (reason.message.includes('critical')) {
      console.log('[CustomSupervisor] Stopping actor due to critical error');
      return SupervisorDirective.Stop;
    } else {
      console.log('[CustomSupervisor] Restarting actor after non-critical error');
      return SupervisorDirective.Restart;
    }
  }
}

// Define an actor that throws errors
class ErrorProneActor implements Actor {
  private errorCount = 0;
  
  constructor(private readonly name: string) {}
  
  receive(context: Context): void {
    const message = context.message();
    
    console.log(`[${this.name}] Received message:`, message);
    
    if (message === 'throw-normal') {
      this.errorCount++;
      throw new Error(`Normal error #${this.errorCount}`);
    } else if (message === 'throw-critical') {
      throw new Error(`critical error that should stop the actor`);
    } else if (message === 'status') {
      console.log(`[${this.name}] Current error count: ${this.errorCount}`);
    } else {
      console.log(`[${this.name}] Processing message: ${message}`);
    }
  }
}

async function main() {
  // Create the actor system
  const system = new ActorSystem();
  
  // Create props with custom supervisor
  const props = Props.fromProducer(() => new ErrorProneActor('TestActor'))
    .withSupervisor(new CustomSupervisor());
  
  // Spawn the actor
  const pid = system.root.spawn(props);
  
  console.log('Actor system started with supervised actor');
  
  // Test the supervision by sending messages that will cause errors
  try {
    // Normal message processing
    system.root.send(pid, 'hello');
    await sleep(500);
    
    // This will throw a normal error and the actor should be restarted
    system.root.send(pid, 'throw-normal');
    await sleep(500);
    
    // Check status after restart
    system.root.send(pid, 'status');
    await sleep(500);
    
    // Another normal message after error
    system.root.send(pid, 'working again');
    await sleep(500);
    
    // This will throw a critical error and the actor should be stopped
    system.root.send(pid, 'throw-critical');
    await sleep(500);
    
    // This should not be processed as the actor has been stopped
    system.root.send(pid, 'hello after critical error');
    await sleep(500);
    
    console.log('Example completed, shutting down system');
    await system.terminate();
  } catch (error) {
    console.error('Error in main:', error);
  }
}

function sleep(ms: number) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

main().catch(console.error); 